#include "sys.h"

color_selector cur_color;

color_selector user_color;

char cap_ascii[128] = {
    [0x0e] = 0x60,
    [0x15] = 0x51,
    [0x16] = 0x31,
    [0x1a] = 0x5a,
    [0x1b] = 0x53,
    [0x1c] = 0x41,
    [0x1d] = 0x57,
    [0x1e] = 0x32,
    [0x21] = 0x43,
    [0x22] = 0x58,
    [0x23] = 0x44,
    [0x24] = 0x45,
    [0x25] = 0x34,
    [0x26] = 0x33,
    [0x29] = 0x20,
    [0x2a] = 0x56,
    [0x2b] = 0x46,
    [0x2c] = 0x54,
    [0x2d] = 0x52,
    [0x2e] = 0x35,
    [0x31] = 0x4e,
    [0x32] = 0x42,
    [0x33] = 0x48,
    [0x34] = 0x47,
    [0x35] = 0x59,
    [0x36] = 0x36,
    [0x3a] = 0x4d,
    [0x3b] = 0x4a,
    [0x3c] = 0x55,
    [0x3d] = 0x37,
    [0x3e] = 0x38,
    [0x41] = 0x2c,
    [0x42] = 0x4b,
    [0x43] = 0x49,
    [0x44] = 0x4f,
    [0x45] = 0x30,
    [0x46] = 0x39,
    [0x49] = 0x2e,
    [0x4a] = 0x2f,
    [0x4b] = 0x4c,
    [0x4c] = 0x3b,
    [0x4d] = 0x50,
    [0x4e] = 0x2d,
    [0x52] = 0x27,
    [0x54] = 0x5b,
    [0x55] = 0x3d,
    [0x5a] = 0x0a,
    [0x5b] = 0x5d,
    [0x5d] = 0x5c,
    [0x66] = 0x08,
    [0x69] = 0x31,
    [0x6b] = 0x34,
    [0x6c] = 0x37,
    [0x70] = 0x30,
    [0x71] = 0x2e,
    [0x72] = 0x32,
    [0x73] = 0x35,
    [0x74] = 0x36,
    [0x75] = 0x38,
    [0x79] = 0x2b,
    [0x7a] = 0x33,
    [0x7b] = 0x2d,
    [0x7c] = 0x2a,
    [0x7d] = 0x39,
};

char cap_sig_ascii[128] = {
    [0x0e] = 0x7e,
    [0x15] = 0x51,
    [0x16] = 0x21,
    [0x1a] = 0x5a,
    [0x1b] = 0x53,
    [0x1c] = 0x41,
    [0x1d] = 0x57,
    [0x1e] = 0x40,
    [0x21] = 0x43,
    [0x22] = 0x58,
    [0x23] = 0x44,
    [0x24] = 0x45,
    [0x25] = 0x24,
    [0x26] = 0x23,
    [0x29] = 0x20,
    [0x2a] = 0x56,
    [0x2b] = 0x46,
    [0x2c] = 0x54,
    [0x2d] = 0x52,
    [0x2e] = 0x25,
    [0x31] = 0x4e,
    [0x32] = 0x42,
    [0x33] = 0x48,
    [0x34] = 0x47,
    [0x35] = 0x59,
    [0x36] = 0x5e,
    [0x3a] = 0x4d,
    [0x3b] = 0x4a,
    [0x3c] = 0x55,
    [0x3d] = 0x26,
    [0x3e] = 0x2a,
    [0x41] = 0x3c,
    [0x42] = 0x4b,
    [0x43] = 0x49,
    [0x44] = 0x4f,
    [0x45] = 0x29,
    [0x46] = 0x28,
    [0x49] = 0x3e,
    [0x4a] = 0x3f,
    [0x4b] = 0x4c,
    [0x4c] = 0x3a,
    [0x4d] = 0x50,
    [0x4e] = 0x5f,
    [0x52] = 0x22,
    [0x54] = 0x7b,
    [0x55] = 0x2b,
    [0x5a] = 0x0a,
    [0x5b] = 0x7d,
    [0x5d] = 0x7c,
    [0x66] = 0x08,
    [0x69] = 0x31,
    [0x6b] = 0x34,
    [0x6c] = 0x37,
    [0x70] = 0x30,
    [0x71] = 0x2e,
    [0x72] = 0x32,
    [0x73] = 0x35,
    [0x74] = 0x36,
    [0x75] = 0x38,
    [0x79] = 0x2b,
    [0x7a] = 0x33,
    [0x7b] = 0x2d,
    [0x7c] = 0x2a,
    [0x7d] = 0x39,
};

char normal_ascii[128] = {
    [0x0e] = 0x60,
    [0x15] = 0x71,
    [0x16] = 0x31,
    [0x1a] = 0x7a,
    [0x1b] = 0x73,
    [0x1c] = 0x61,
    [0x1d] = 0x77,
    [0x1e] = 0x32,
    [0x21] = 0x63,
    [0x22] = 0x78,
    [0x23] = 0x64,
    [0x24] = 0x65,
    [0x25] = 0x34,
    [0x26] = 0x33,
    [0x29] = 0x20,
    [0x2a] = 0x76,
    [0x2b] = 0x66,
    [0x2c] = 0x74,
    [0x2d] = 0x72,
    [0x2e] = 0x35,
    [0x31] = 0x6e,
    [0x32] = 0x62,
    [0x33] = 0x68,
    [0x34] = 0x67,
    [0x35] = 0x79,
    [0x36] = 0x36,
    [0x3a] = 0x6d,
    [0x3b] = 0x6a,
    [0x3c] = 0x75,
    [0x3d] = 0x37,
    [0x3e] = 0x38,
    [0x41] = 0x2c,
    [0x42] = 0x6b,
    [0x43] = 0x69,
    [0x44] = 0x6f,
    [0x45] = 0x30,
    [0x46] = 0x39,
    [0x49] = 0x2e,
    [0x4a] = 0x2f,
    [0x4b] = 0x6c,
    [0x4c] = 0x3b,
    [0x4d] = 0x70,
    [0x4e] = 0x2d,
    [0x52] = 0x27,
    [0x54] = 0x5b,
    [0x55] = 0x3d,
    [0x5a] = 0x0a,
    [0x5b] = 0x5d,
    [0x5d] = 0x5c,
    [0x66] = 0x08,
    [0x69] = 0x31,
    [0x6b] = 0x34,
    [0x6c] = 0x37,
    [0x70] = 0x30,
    [0x71] = 0x2e,
    [0x72] = 0x32,
    [0x73] = 0x35,
    [0x74] = 0x36,
    [0x75] = 0x38,
    [0x79] = 0x2b,
    [0x7a] = 0x33,
    [0x7b] = 0x2d,
    [0x7c] = 0x2a,
    [0x7d] = 0x39,
};

char sig_ascii[128] = {
    [0x0e] = 0x7e,
    [0x15] = 0x51,
    [0x16] = 0x21,
    [0x1a] = 0x5a,
    [0x1b] = 0x53,
    [0x1c] = 0x41,
    [0x1d] = 0x57,
    [0x1e] = 0x40,
    [0x21] = 0x43,
    [0x22] = 0x58,
    [0x23] = 0x44,
    [0x24] = 0x45,
    [0x25] = 0x24,
    [0x26] = 0x23,
    [0x29] = 0x20,
    [0x2a] = 0x56,
    [0x2b] = 0x46,
    [0x2c] = 0x54,
    [0x2d] = 0x52,
    [0x2e] = 0x25,
    [0x31] = 0x4e,
    [0x32] = 0x42,
    [0x33] = 0x48,
    [0x34] = 0x47,
    [0x35] = 0x59,
    [0x36] = 0x5e,
    [0x3a] = 0x4d,
    [0x3b] = 0x4a,
    [0x3c] = 0x55,
    [0x3d] = 0x26,
    [0x3e] = 0x2a,
    [0x41] = 0x3c,
    [0x42] = 0x4b,
    [0x43] = 0x49,
    [0x44] = 0x4f,
    [0x45] = 0x29,
    [0x46] = 0x28,
    [0x49] = 0x3e,
    [0x4a] = 0x3f,
    [0x4b] = 0x4c,
    [0x4c] = 0x3a,
    [0x4d] = 0x50,
    [0x4e] = 0x5f,
    [0x52] = 0x22,
    [0x54] = 0x7b,
    [0x55] = 0x2b,
    [0x5a] = 0x0a,
    [0x5b] = 0x7d,
    [0x5d] = 0x7c,
    [0x66] = 0x08,
    [0x69] = 0x31,
    [0x6b] = 0x34,
    [0x6c] = 0x37,
    [0x70] = 0x30,
    [0x71] = 0x2e,
    [0x72] = 0x32,
    [0x73] = 0x35,
    [0x74] = 0x36,
    [0x75] = 0x38,
    [0x79] = 0x2b,
    [0x7a] = 0x33,
    [0x7b] = 0x2d,
    [0x7c] = 0x2a,
    [0x7d] = 0x39,
};

char* vga_start = (char*)VGA_START;
char vga_line = 0;
char* base_line = (char*)VGA_LINE_O;
int vga_ch = 0;
unsigned char* key_buf = (unsigned char*)KEY_START;
volatile char* key_tail = (char*)KEY_TAIL;
volatile char* key_caps = (char*)KEY_CAPS;
volatile char* key_shift = (char*)KEY_SHIFT;
volatile char head = 0;
unsigned long long* time_head = (unsigned long long*)TIME_HEAD;
uint8_t* color_bucket = (uint8_t*)VGA_COLOR;
int* vga_mouse = (int*)VGA_MOUSE;
bool mouse_invisible = 0;
char unknown[] = "Error Input";
;

char* itoa(int i, char* a, int base)
{
    if (i == 0) {
        a[0] = '0';
        a[1] = '\0';
        return a;
    }
    if (base == 16) {
        int cnt = 2;
        a[0] = '0';
        a[1] = 'x';
        while (i != 0) {
            int tmp = i & 0xf;
            if (tmp <= 9) {
                tmp = tmp + '0';
            } else {
                tmp = tmp - 10 + 'a';
            }
            a[cnt] = tmp;
            i >>= 4;
            ++cnt;
        }
        a[cnt] = '\0';
        int head = 2, tail = cnt - 1;
        while (head < tail) {
            int tmp = a[head];
            a[head] = a[tail];
            a[tail] = tmp;
            ++head;
            --tail;
        }
        return a;
    } else {
        int cnt = 0;
        while (i) {
            int tmp = __umodsi3(i, 10);
            tmp = tmp + '0';
            a[cnt++] = tmp;
            i = __udivsi3(i, 10);
        }
        a[cnt] = '\0';
        int head = 0, tail = cnt - 1;
        while (head < tail) {
            int tmp = a[head];
            a[head] = a[tail];
            a[tail] = tmp;
            ++head;
            --tail;
        }
        return a;
    }
}

int fib(int n)
{
    int t1 = 0, t2 = 1;
    while (n--) {
        int tmp = t2;
        t2 = t1 + t2;
        t1 = tmp;
    }
    return t2;
}

struct KEY transform(int u)
{
    struct KEY k;
    k.magic = 0;
    if (*key_shift && *key_caps) {
        k.c = cap_sig_ascii[u];
    } else if (*key_shift) {
        k.c = sig_ascii[u];
    } else if (*key_caps) {
        k.c = cap_ascii[u];
    } else
        k.c = normal_ascii[u];
    return k;
}

int atoi(char* a, int base)
{
    int i = 0;
    if (base == 16) {
        while (*a != 0) {
            int tmp = *a;
            if (tmp <= '9') {
                tmp = tmp - '0';
            } else {
                tmp = tmp - 'a' + 10;
            }
            i = (i << 4) + tmp;
            ++a;
        }
    } else {
        while (*a) {
            int tmp = *a;
            tmp = tmp - '0';
            if (tmp < 0 || tmp > 9) {
                return i;
            }
            i = mul(i, base) + tmp;
            ++a;
        }
    }
    return i;
}

void vga_init()
{
    (*base_line) = 0;
    vga_line = 0;
    vga_ch = 0;
    cur_color.fcolor = cur_color.bcolor = 0;
    user_color.fcolor = user_color.bcolor = 0;
    for (int i = 0; i < VGA_MAXLINE; i++)
        for (int j = 0; j < VGA_MAXCOL; j++) {
            set_color((i << 6) + (i << 2) + (i << 1) + j, cur_color);
            vga_start[(i << 6) + (i << 2) + (i << 1) + j] = 0;
        }
}

void clear_line(int lineno)
{
    color_selector c;
    c.fcolor = c.bcolor = 0;
    for (int i = 0; i < VGA_MAXCOL; ++i) {
        set_color((lineno << 6) + (lineno << 2) + (lineno << 1) + i, c);
        vga_start[(lineno << 6) + (lineno << 2) + (lineno << 1) + i] = 0;
    }
}

void incre_head()
{
    if (head == 15)
        head = 0;
    else
        ++head;
}

void vga_incre()
{
    int place = (vga_line + *base_line) & LINE_MASK;
    vga_ch++;
    if (vga_ch >= VGA_MAXCOL) {
        ++vga_line;
        if (vga_line == VGA_SCREEN_LINE) {
            ++(*base_line);
            clear_line(*base_line - 1);
            if (*base_line == VGA_MAXLINE) {
                *base_line = 0;
            }
            --vga_line;
        }
        vga_ch = 0;
    }
    if (mouse_invisible)
        *vga_mouse = 114514;
    else
        *vga_mouse = ((place) << 6) + ((place) << 2) + ((place) << 1) + vga_ch;
    return;
}

KEY getch()
{
    int c;
    volatile int not_ready = head == *key_tail;
    while (not_ready) {
        not_ready = head == *key_tail;
    };
    c = key_buf[head];
    KEY k;
    incre_head();
    if (c == 0x75) { // up pressed
        k.magic = -1;
        k.c = c;
    } else if (c == 0x72) { // down pressed
        k.magic = -1;
        k.c = c;
    } else if (c == 0x74) { // right pressed
        k.magic = -1;
        k.c = c;
    } else if (c == 0x6b) { // left pressed
        k.magic = -1;
        k.c = c;
    } else {
        k = transform(c);
    }
    return k;
}

int getr()
{
    int c;
    while (head == *key_tail)
        ;
    do {
        c = key_buf[head];
    } while (c == 0x12 || c == 0x58);
    if (head == 15)
        head = 0;
    else
        ++head;
    return c;
}

void putch(char ch, int color)
{

    int place = (vga_line + *base_line) & LINE_MASK;
    if (color) {
        set_color(((place) << 6) + ((place) << 2) + ((place) << 1) + vga_ch,
            cur_color);
    } else {
        set_color(((place) << 6) + ((place) << 2) + ((place) << 1) + vga_ch,
            user_color);
    }
    if (ch == 8) // backspace
    {
        if (vga_ch > 0) {
            vga_ch--;
        } else {
            if (vga_line > 0) {
                vga_line--;
                vga_ch = VGA_MAXCOL - 1;
            }
        }
    } else if (ch == 10) // enter
    {
        ++vga_line;
        if (vga_line == VGA_SCREEN_LINE) {
            ++(*base_line);
            clear_line(*base_line - 1);
            if (*base_line == VGA_MAXLINE) {
                *base_line = 0;
            }
            --vga_line;
        }
        vga_ch = 0;
    } else if (ch == '\t') { // a tab must be 2 spaces wide.
        putstr("  ", color);
    } else {
        vga_start[((place) << 6) + ((place) << 2) + ((place) << 1) + vga_ch] = ch;
        vga_ch++;
        if (vga_ch >= VGA_MAXCOL) {
            ++vga_line;
            if (vga_line == VGA_SCREEN_LINE) {
                ++(*base_line);
                clear_line(*base_line - 1);
                if (*base_line == VGA_MAXLINE) {
                    *base_line = 0;
                }
                --vga_line;
            }
            vga_ch = 0;
        }
    }
    if (mouse_invisible)
        *vga_mouse = 114514;
    else
        *vga_mouse = ((place) << 6) + ((place) << 2) + ((place) << 1) + vga_ch;
    return;
}

void putstr(char* str, int color)
{
    for (char* p = str; *p != 0; ++p)
        putch(*p, color);
}

void puts(char* str, int color)
{
    putstr(str, color);
    putch('\n', color);
}

void gets(char* welcome1, char* welcome2, char* dest)
{
    cur_color.fcolor = 2;
    cur_color.bcolor = 0;
    int place = (vga_line + *base_line) & LINE_MASK;
    int max_tail = 0;
    int cur_index = 0;
    putstr(welcome1, 1);
    cur_color.fcolor = 0;
    putstr(":", 1);
    cur_color.fcolor = 5;
    putstr(welcome2, 1);
    cur_color.fcolor = 0;
    cur_color.bcolor = 0;
    putstr("$ ", 1);
    int tail = 0;
    KEY c = getch();
    while (c.c != '\n') {
        if (c.magic == -1) {
            switch (c.c) {
            case 0x75: {
                break;
            }
            case 0x72: {
                break;
            }
            case 0x74: {
                if (dest[tail]) {
                    tail++;
                    vga_incre();
                }
                break;
            }
            case 0x6b: {
                if (tail > 0) {
                    tail--;
                    putch('\b', 0);
                }
                break;
            }
            }
            c = getch();
            continue;
        }
        if (c.c == 0x08) {
            if (tail > 0) {
                dest[--tail] = '\0';
                if (tail > max_tail)
                    max_tail = tail;
                color_selector c = user_color;
                user_color.fcolor = user_color.bcolor = 0;
                putstr("\b \b", 0);
                user_color = c;
            }
        } else {
            dest[tail++] = c.c;
            if (tail > max_tail)
                max_tail = tail;
            putch(c.c, 0);
        }
        c = getch();
    }
    if (max_tail)
        putch('\n', 0);
    dest[max_tail] = '\0';
}

TIME time()
{
    uint64_t time_now = *time_head;
    TIME t;
    t.val = time_now;
    t.ms = mod(time_now, 1000);
    time_now = div(time_now, 1000);
    t.s = mod(time_now, 60);
    time_now = div(time_now, 60);
    t.min = time_now;
    time_now = div(time_now, 60);
    t.hr = mod(time_now, 24);
    time_now = div(time_now, 24);
    t.day = time_now;
    return t;
}

void set_color(uint32_t addr, color_selector color)
{
    color_bucket[addr] = color.val;
}